home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
search
/
lsmtool-.6
/
lsmtool-
/
lsmtool-0.6
/
lsm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-27
|
10KB
|
460 lines
/*
* lsm.c -- routines for manipulating LSM entries and data bases
*
* Lars Wirzenius
* "@(#)lsmtool:lsm.c,v 1.9 1994/12/27 15:57:14 wirzeniu Exp"
*/
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <publib.h>
#include "lsm.h"
static int valid_email(const char *);
static int valid_date(const char *);
static int valid_site(const char *);
/*
* Is a field empty?
*/
static int field_is_empty(const char *s) {
return s != NULL && (s[strspn(s, " \t\n")] == '\0');
}
/*
* This array gives the names of the fields. There is room for user-defined
* fields. The array is initialized with those field names whose order
* is predefined when displayed (i.e., they happen to look good in this
* order).
*/
static struct lsm_entry field_names = {
"Title",
"Version",
"Entered-date",
"Description",
"Keywords",
"Author",
"Maintained-by",
"Primary-site",
"Alternate-site",
"Original-site",
"Platforms",
"Copying-policy",
};
/*
* Find the index for a field name. If the field name isn't there already,
* add it to the table. Return the index; kill program on error (name not
* in table, can't be added).
*/
int lsm_field_to_index(const char *s) {
int i;
for (i = 0; i < MAX_FIELDS; ++i)
if (strcmp(s, field_names.fields[i]) == 0)
return i;
for (i = 0; i < MAX_FIELDS && field_names.fields[i] != NULL; ++i)
continue;
if (i == MAX_FIELDS)
errormsg(1, 0, "Too many fields (max is %d)", MAX_FIELDS);
field_names.fields[i] = xstrdup(s);
return i;
}
/*
* Return the field name corresponding to a given index.
*/
const char *lsm_index_to_field(int i) {
assert(i >= 0);
assert(i < MAX_FIELDS);
return field_names.fields[i];
}
/*
* Is this line the "End" line?
*/
static int is_end_line(const char *line) {
if (strncmp(line, "End", 3) != 0)
return 0;
for (line += 3; isspace(*line); ++line)
continue;
return *line == '\0';
}
/*
* Read one entry from the file f. Skip all leading garbage, but don't
* read past the `End' line. Return 0 for no entry found, >0 for ok,
* -1 for error.
*/
int lsm_read_one_entry(FILE *f, long *lineno, struct lsm_entry *e) {
int i, prev;
char *p, *q;
assert(f != NULL);
assert(lineno != NULL);
assert(*lineno >= 1);
assert(e != NULL);
/* Initialize the entry */
for (i = 0; i < MAX_FIELDS; ++i)
e->fields[i] = NULL;
/* Find "Begin3" that starts the entry. */
for (;;) {
p = getaline(f);
if (p == NULL) {
if (ferror(f) || !feof(f))
return -1;
return 0;
}
++(*lineno);
if (strcmp(p, "Begin3") == 0) {
free(p);
break; /* memory leak? */
}
free(p);
}
/* Read until "End" that ends the entry. */
prev = 0;
for (;;) {
p = getaline(f);
if (p == NULL) {
if (ferror(f) || !feof(f))
return -1;
errormsg(0, 0, "Missing `End' line\n");
return -1;
}
++(*lineno);
if (is_end_line(p)) {
for (i = 0; i < MAX_FIELDS; ++i) {
if (field_is_empty(e->fields[i])) {
free(e->fields[i]);
e->fields[i] = NULL;
}
}
return 1;
}
if (isspace(*p) || *p == '\0') {
char *t;
q = e->fields[prev];
if (q == NULL) {
errormsg(0, 0, "Line %ld has no keyword",
*lineno);
return -1;
}
t = p;
e->fields[prev] = stracat(q, t, "\n", (char*)NULL);
if (e->fields[prev] == NULL) {
errormsg(0, -1,
"Out of memory? stracat failed");
return -1;
}
free(p);
free(q);
} else {
q = strchr(p, ':');
if (q == NULL) {
errormsg(0, 0,
"No keyword on line %ld", *lineno);
return -1;
}
*q++ = '\0';
i = lsm_field_to_index(p);
if (e->fields[i] != NULL) {
errormsg(0, 0,
"Same keyword many times on line %ld",
*lineno);
return -1;
}
e->fields[i] = stracat(q, "\n", (char *)NULL);
if (e->fields[i] == NULL) {
errormsg(0, -1, "out of mem? stracat failed");
return -1;
}
free(p);
prev = i;
}
}
}
/*
* Read a whole database, i.e., a file with many entries in it. Return
* -1 for error (even if any entries were * read), 0 for ok. The entries
* are appended to those that already exist in the database. Nothing
* is sorted.
*
* Support for reading old format entries may be added in the future.
*/
int lsm_read_database(FILE *f, struct lsm_database *db) {
struct lsm_entry *p;
size_t newsize;
long lineno;
int ret;
const size_t inc = 1024;
assert(f != NULL);
assert(db != NULL);
lineno = 1;
for (;;) {
assert(db->nentries <= db->nalloc);
if (db->nentries == db->nalloc) {
newsize = db->nalloc + inc;
p = realloc(db->entries, newsize * sizeof(*p));
if (p == NULL) {
errormsg(0, -1, "realloc failed");
return -1;
}
db->entries = p;
db->nalloc = newsize;
}
ret = lsm_read_one_entry(f, &lineno, db->entries+db->nentries);
if (ret <= 0)
return ret;
++db->nentries;
}
}
/*
* Write one entry (in new format) to a file.
*/
int lsm_write_one_entry(FILE *f, const struct lsm_entry *e) {
int i;
fprintf(f, "Begin3\n");
for (i = 0; i < MAX_FIELDS; ++i)
if (e->fields[i] != NULL)
fprintf(f, "%s:%s",
field_names.fields[i], e->fields[i]);
fprintf(f, "End\n\n");
if (ferror(f))
return -1;
return 0;
}
/*
* Write a whole database to the file.
*/
int lsm_write_database(FILE *f, struct lsm_database *db) {
size_t i;
for (i = 0; i < db->nentries; ++i)
if (lsm_write_one_entry(f, &db->entries[i]) == -1)
return -1;
if (fflush(f) == EOF || ferror(f))
return -1;
return 0;
}
/*
* Check that all mandatory fields are present, and that fields with
* special syntax follow it. Return 0 for ok, -1 for error; print
* appropriate error messages.
*/
int lsm_check_entry(struct lsm_entry *e, const char *msgprefix) {
static const char *mandatory[] = {
"Title",
"Version",
"Description",
"Primary-site",
};
static const char *emails[] = {
"Author",
"Maintained-by",
};
static const char *dates[] = {
"Entered-date",
"Checked-date",
};
static const char *sites[] = {
"Primary-site",
"Alternate-site",
"Original-site",
};
static const char *usual[] = {
"Title",
"Version",
"Description",
"Keywords",
"Author",
"Maintained-by",
"Primary-site",
"Alternate-site",
"Original-site",
"Platforms",
"Copying-policy",
"Entered-date",
"Checked-status",
"Checked-date",
};
struct lsm_entry ee;
int i, j, ret;
assert(e != NULL);
assert(msgprefix != NULL);
ret = 0;
/* Check that all mandatory fields are present. */
for (i = 0; i < sizeof(mandatory)/sizeof(char *); ++i) {
j = lsm_field_to_index(mandatory[i]);
if (e->fields[j] == NULL) {
errormsg(0, 0, "%s%sMandatory field `%s' is missing.",
msgprefix, *msgprefix ? ": " : "",
mandatory[i]);
ret = -1;
}
}
/* Check for extra fields. */
for (i = 0; i < MAX_FIELDS; ++i)
ee.fields[i] = NULL;
for (i = 0; i < sizeof(usual)/sizeof(char *); ++i) {
j = lsm_field_to_index(usual[i]);
ee.fields[j] = "";
}
for (i = 0; i < MAX_FIELDS; ++i) {
if (ee.fields[i] == NULL && e->fields[i] != NULL) {
errormsg(0, 0, "%s%sExtra field `%s'.",
msgprefix, *msgprefix ? ": " : "",
lsm_index_to_field(i));
ret = -1;
}
}
/* Check all e-mail fields. */
for (i = 0; i < sizeof(emails)/sizeof(char *); ++i) {
j = lsm_field_to_index(emails[i]);
if (j >= 0 && !valid_email(e->fields[j])) {
errormsg(0, 0,
"%s%sBad email address `%.*s' in field `%s'?",
msgprefix, *msgprefix ? ": " : "",
(int)strcspn(e->fields[j], "\n"),
e->fields[j], emails[i]);
ret = -1;
}
}
/* Check dates */
for (i = 0; i < sizeof(dates)/sizeof(char *); ++i) {
j = lsm_field_to_index(dates[i]);
if (j >= 0 && !valid_date(e->fields[j])) {
errormsg(0, 0, "%s%sBad date `%.*s' in field `%s'",
msgprefix, *msgprefix ? ": " : "",
(int)strcspn(e->fields[j], "\n"),
e->fields[j], dates[i]);
ret = -1;
}
}
/* Check the sites */
for (i = 0; i < sizeof(sites)/sizeof(char *); ++i) {
j = lsm_field_to_index(sites[i]);
if (j >= 0 && !valid_site(e->fields[j])) {
errormsg(0, 0, "%s%sBad site spec `%.*s' in field `%s'",
msgprefix, *msgprefix ? ": " : "",
(int)strcspn(e->fields[j], "\n"),
e->fields[j], sites[i]);
ret = -1;
}
}
return ret;
}
/*
* Check whether an e-mail address is valid. A very simple, but hopefully
* effective enough method is making sure there is an `@' character in it.
* Full checking would be much more troublesome.
*/
static int valid_email(const char *s) {
#if 0
return s == NULL || strchr(s, '@') != NULL;
#else
return 1;
#endif
}
/*
* Check that a date is valid, i.e., in ddMMMyy format (where MMM is a
* three letter, upper case abbreviation of * a month).
*/
static int valid_date(const char *s) {
int dd, mm, yy, leap;
static const char *months[12] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};
static const int monlen[2][12] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
};
if (s == NULL)
return 1;
while (isspace(*s))
++s;
if (!isdigit(s[0]) || !isdigit(s[1]))
return 0;
if (!isdigit(s[5]) || !isdigit(s[6]))
return 0;
dd = 10*(s[0]-'0') + (s[1]-'0');
yy = 10*(s[5]-'0') + (s[6]-'0') + 1900;
for (mm = 0; mm < 12; ++mm)
if (strncasecmp(s+2, months[mm], 3) == 0)
break;
if (mm == 12)
return 0;
leap = (yy % 4) == 0 && ((yy % 100) != 0 || (yy % 400) == 0);
if (mm < 0 || mm > 12 || dd < 0 || dd > monlen[leap][mm])
return 0;
return 1;
}
/*
* Check that a site specification is valid.
*/
static int valid_site(const char *s) {
return 1; /* let's take it easy for now. */
}